#!/usr/bin/env python
# note.py

import pygame
import os
import sys

black = 0, 0, 0
white = 255, 255, 255
gray = 60, 60, 60
red = 255, 0, 0

screen_size = screen_width, screen_height = 320, 240

class Cursor:
    width = 20 
    height = 20 

    delta_x = 20 
    delta_y = 10

    def __init__(self, x = 0, y = 0):
        self.rect = pygame.Rect(x, y, Cursor.width, Cursor.height)

    def draw(self, surface):
        pygame.draw.rect(surface, gray, self.rect)

    def move_left(self):
        self.rect.move_ip(-Cursor.delta_x, 0)

    def move_right(self):
        self.rect.move_ip(Cursor.delta_x, 0)

    def move_up(self):
        self.rect.move_ip(0, -Cursor.delta_y)

    def move_down(self):
        self.rect.move_ip(0, Cursor.delta_y)

    def position(self):
        (x, y) = self.rect.topleft
        x /= Cursor.delta_x
        y /= Cursor.delta_y
        return (x, y)


class Note:
#
# position
# 0-31
# The position in the measure
#
# duration is a unit,
# where a measue is 32 units long.
# e.g. 8 = quarter note
#
# pitch is a midi number from 0-127
# e.g. 60 = middle C (octave 4)
#
    def __init__(self, pitch = 60, duration = 0, position = (0, 0)):
        self.pitch = pitch
        self.duration = duration
        self.position = position

    def draw(self, surface, position):
        relative_pitch = self.pitch - 67   # starts at high G

        (x, y) = position
        x += self.position * Cursor.delta_x
        y += relative_pitch * Cursor.delta_y

        note_rect = Cursor(x, y).rect
        pygame.draw.circle(surface, red, note_rect.center, 10)


class Measure:
#
# A measure is a list of elements.
#
# element = e.g. note, rest, etc.
#
    units = 32

    def __init__(self):
        self.elements = []

    def insert_note(self, note):
        self.elements.append(note)


class Staff:
#
# collection of measures
#
    num_grid_lines = 5

    def __init__(self):
        self.measures = []

    def insert_note(self, note, measure_number):
        while len(self.measures) <= measure_number:
            self.measures.append(Measure())

        measure = self.measures[measure_number]
        measure.insert_note(note)


class StaffView:
    height = Cursor.height * Staff.num_grid_lines

    def __init__(self, staff, row):
        self.current_measure = 0
        self.staff = staff
        self.row = 0
        self.current_duration = 8 # XXX

    def draw(self, surface):
        # draw gridlines
        row = self.row * 100

        for y in (20, 40, 60, 80, 100):
            y += row
            pygame.draw.line(surface, black, (1, y), (screen_width, y))

        x, y = (0, row)

        # draw notes
        for relative_measure_number in range(0, 4):
            measure_number = self.current_measure + relative_measure_number
            if measure_number < 0 or measure_number >= len(self.staff.measures):
                break

            measure = self.staff.measures[measure_number]

            for element in measure.elements:
                x = (Cursor.delta_x * Measure.units) * relative_measure_number
                position = (x, y)
                element.draw(surface, position)


    def insert_note(self, position):
        (x, y) = position
        position_in_measure = x % 32
        relative_measure_number = x / 32
        measure_number = self.current_measure + relative_measure_number

        relative_pitch = y - self.row * StaffView.height
        pitch = relative_pitch + 67

        note = Note(pitch, self.current_duration, position_in_measure)

        self.staff.insert_note(note, measure_number)


pygame.init()

screen = pygame.display.set_mode(screen_size)
cursor = Cursor()

staff = Staff()
staff_view = StaffView(staff, 0)

while 1:
    event = pygame.event.wait()

    if event.type == pygame.QUIT:
        sys.exit()
    elif event.type == pygame.KEYDOWN:
        key = event.key
        if key == pygame.K_ESCAPE:
            sys.exit()
        elif key == pygame.K_j:
            cursor.move_down()
        elif key == pygame.K_k:
            cursor.move_up()
        elif key == pygame.K_l:
            cursor.move_right()
        elif key == pygame.K_h:
            cursor.move_left()
        elif key == pygame.K_SPACE:
            staff_view.insert_note(cursor.position())

    screen.fill(white)
    cursor.draw(screen)
    staff_view.draw(screen)
    pygame.display.flip()


# Staff
#
# List of measures
#
# staff = [measure1, measure2, ...]

# Measure
#
# object
# one of the following: ....
#
# measure = [rest1, slur_begin, note1, note2, slur_end]


# Note
#
# position
# 0-31
# The position in the measure
#
# duration
# A measure is 32 units long.
# e.g. 8 = quarter note
#
# value
# a MIDI number from 0-127
# e.g. 60 = middle C (octave 4)
#
# note = (duration, value)
